#include "CLogger.h"
#include <QThread>
#include <QDebug>
#include <iostream>
#include <QFileInfo>

void outputMsg(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    static int arr[] = {0,2,3,4,1};
    LogLevel level = (LogLevel)arr[type]; //重置Qt级别顺序

    QString log = qFormatLogMessage(type,context,msg);
    CLogger::getInstance()->slotAppendLog(level,log);
}

CLogger::CLogger(QObject *parent) : QObject(parent)
{
    init();

    qInstallMessageHandler(outputMsg);
}

CLogger *CLogger::getInstance()
{
    static CLogger* pInstance = nullptr;
    if(nullptr == pInstance){
        static QMutex mutex;
        static QMutexLocker locker(&mutex);
        pInstance = new CLogger();
    }
    return pInstance;
}

void CLogger::init()
{
    //读取配置文件
    QString strIniFile = QCoreApplication::applicationDirPath() + "/LogConfog.ini";
    bool bExists = QFile(strIniFile).exists();
    m_pSettings = new QSettings(strIniFile,QSettings::IniFormat);

    //配置文件不存在 创建默认的配置文件
    if(false == bExists){
        //配置文件说明
        m_pSettings->beginGroup("Readme");
        m_pSettings->setValue("SupportFmt","See qSetMessagePattern");
        m_pSettings->setValue("Level","Debug 0 Info 1 Warning 2 Error 3 Fatal 4");
        m_pSettings->setValue("FileSizeUnits","MB");
        m_pSettings->endGroup();

        //全局日志格式
        m_pSettings->beginGroup("Global");
        m_pSettings->setValue("Pattern",LOG_FMT);
        m_pSettings->endGroup();

        m_pSettings->beginGroup("Console");
        m_pSettings->setValue("ConsoleEnable",true);
        m_pSettings->setValue("ConsoleLevel",0);
        m_pSettings->endGroup();

        m_pSettings->beginGroup("Udp");
        m_pSettings->setValue("UdpEnable",true);
        m_pSettings->setValue("UdpLevel",0);
        m_pSettings->setValue("UdpRemoteIp","127.0.0.1");
        m_pSettings->setValue("UdpRemotePort",9009);
        m_pSettings->endGroup();

        m_pSettings->beginGroup("File");
        m_pSettings->setValue("FileEnable",true);
        m_pSettings->setValue("FileLevel",0);
        m_pSettings->setValue("FileDir",LOG_DIR);
        m_pSettings->setValue("FileMaxSize",200);
        m_pSettings->setValue("FileMaxCount",10);
        m_pSettings->endGroup();
    }

    //设置日志格式
    qSetMessagePattern(m_pSettings->value("/Global/Pattern").toString());

    //创建控制台日志器
    if(m_pSettings->value("Console/ConsoleEnable",true).toBool()){
        LogLevel level = (LogLevel)m_pSettings->value("Console/ConsoleLevel",0).toInt();

        m_pConsoleAppender = new CConsoleAppender(level);

        m_listAppender.append(m_pConsoleAppender);
    }

    //创建Udp日志器
    if(m_pSettings->value("Udp/UdpEnable",false).toBool()){
        LogLevel level = (LogLevel)m_pSettings->value("Udp/UdpLevel",0).toInt();
        QString strIp = m_pSettings->value("Udp/UdpRemoteIp","127.0.0.1").toString();
        quint16 nPort = m_pSettings->value("Udp/UdpRemotePort",9009).toInt();

        m_pUdpAppender = new CUdpAppender(level,strIp,nPort);

        m_listAppender.append(m_pUdpAppender);
    }

    //创建文件日志器
    if(m_pSettings->value("File/FileEnable",true).toBool()){
        LogLevel level = (LogLevel)m_pSettings->value("File/FileLevel",0).toInt();
        QString strFileDir = m_pSettings->value("File/FileDir",LOG_DIR).toString();
        QDir dirLog(strFileDir);
        if(!dirLog.exists()){
            strFileDir = LOG_DIR;
            m_pSettings->setValue("File/FileDir",LOG_DIR);
        }
        int nMB = m_pSettings->value("File/FileMaxSize",200).toInt();
        int nMaxCount = m_pSettings->value("File/FileMaxCount",10).toInt();

        m_pFileAppender = new CFileAppender(level,strFileDir);
        m_pFileAppender->setMaxSize(nMB);
        m_pFileAppender->setMaxCout(nMaxCount);

        m_listAppender.append(m_pFileAppender);
    }
}

void CLogger::slotAppendLog(LogLevel level,const QString &msg)
{
    Q_UNUSED(level)
    static QMutex mutex;
    foreach (CAppender *appender, CLogger::getInstance()->m_listAppender) {
        mutex.lock();
        appender->append(level,msg);
        mutex.unlock();
    }
}

void CConsoleAppender::append(LogLevel level, const QString &msg)
{
    if(level < m_enmLevel)
        return ;

    fprintf(stdout,"%s \n",msg.toUtf8().data());
    fflush(stdout);
}

CUdpAppender::CUdpAppender(LogLevel level, QString strRemoteIp, quint16 nRemotePort)
    :CAppender(level),m_strRemoteIp(strRemoteIp),m_nRemotePort(nRemotePort)
{
    m_pUdp = new QUdpSocket();
    m_pUdp->bind(QHostAddress::LocalHost, 7755);
}

CUdpAppender::~CUdpAppender()
{
    if(nullptr != m_pUdp){
        m_pUdp->close();
        m_pUdp->deleteLater();
    }
}

void CUdpAppender::append(LogLevel level, const QString &msg)
{
    if(level < m_enmLevel)
        return ;

    m_pUdp->writeDatagram(msg.toUtf8().constData(),
                          QHostAddress(m_strRemoteIp),m_nRemotePort);
}



CFileAppender::CFileAppender(LogLevel level, QString strFilePath)
    : CAppender(level),m_strLogPath(strFilePath)
{
    setLogPath(m_strLogPath);
}

void CFileAppender::setLogPath(QString strPath)
{
    m_strLogPath = strPath;

    //检查日志目录是否存在
    QDir logDir(m_strLogPath);
    if(!logDir.exists()){
        if(!logDir.mkpath(m_strLogPath))
            std::cout<<"Log path create failed!"<<std::endl;
    }

    //设定日志输出文件
    redirectLog(m_fileLog);
}

void CFileAppender::append(LogLevel level,const QString &msg)
{
    if(level < m_enmLevel)
        return ;
    if(m_fileLog.open(QIODevice::WriteOnly | QIODevice::Append)){
        m_fileLog.write(msg.toUtf8() + "\n");
//        std::cout<<"Write log file:"<<m_fileLog.fileName().toStdString()<<std::endl;
    }else{
        std::cout<<"Log file open failed! File:"<<m_fileLog.fileName().toStdString()<<std::endl;
        return ;
    }
    m_fileLog.close();

    //判断是否需要重定向日志输出 @MAJR 如何比较好的减少判断次数？
    if(isNeedRedirect()){
        redirectLog(m_fileLog);
    }
}

void CFileAppender::redirectLog(QFile &file)
{
    //日志文件名构成 日期 + 当日日志计数 + .txt  @Note当天的日志 会在日期前加Today
    QString strToday = QDateTime::currentDateTime().toString("yyyy-MM-dd");

    QStringList strListTodayLogs;
    QFileInfoList listInfoTodayLogs;
    QFileInfoList strListLogs = entryInfoListFotCreate(m_strLogPath); //创建日期排序
    foreach (QFileInfo fileInfo, strListLogs) {
        if(fileInfo.fileName().startsWith("Today_")){
            //非今日的日志  但还未去掉Today_前缀  直接去掉Today标记
            if(fileInfo.fileTime(QFileDevice::FileBirthTime).date() != QDateTime::currentDateTime().date()){
                QString strNewName = fileInfo.filePath().replace("Today_","");
                if(false == QFile::rename(fileInfo.filePath(),strNewName)){ //重命名失败 删除重名文件
                        std::cout<<"[Waring] Rename :"<<fileInfo.filePath().toStdString()<<" to \n"<<strNewName.toStdString()<<" failed!"<<std::endl;
                       if(QFile::remove(strNewName)){
                           QFile::rename(fileInfo.filePath(),strNewName);
                       }else{
                           std::cout<<"[Waring] Remove failed!"<<std::endl;
                       }
                }
            }else{
                strListTodayLogs.append(fileInfo.filePath());
                listInfoTodayLogs.append(fileInfo);
            }
        }
    }

    int m_nTodayCount = 0; //今日日志总数
    int nRedirectNum = 0;  //重定向后的编号
    //获取今日最后的日志
    if(!listInfoTodayLogs.isEmpty()){
        //文件过大 需要新创建
        if(listInfoTodayLogs.last().size() > m_nMaxSize){
            m_nTodayCount = listInfoTodayLogs.count();
        }else{ //不过大 直接定位到此文件
            file.setFileName(listInfoTodayLogs.last().filePath());
            return ;
        }
    }

    //需要创建新文件 检查文件是否存在  存在 编号++
    QString strLogName = "";
    do{
        nRedirectNum = ++m_nTodayCount;  //@MAJR 应取最后一个日志的编号
        strLogName = QString("%1/Today_%2_%3.txt").arg(m_strLogPath).arg(strToday).arg(nRedirectNum);
    }while(QFile(strLogName).exists());

    //检查日志文件个数是否超出
    if (strListLogs.count() >= m_nMaxCount) {
        QFileInfoList strCurrListLogs = entryInfoListFotCreate(m_strLogPath); //重新获取所有日志 创建日期排序
        while(strCurrListLogs.count() >= m_nMaxCount){
            QString strOldLog = strCurrListLogs.takeFirst().filePath();
            QFile::remove(strOldLog); //删除失败无所谓  接着删除后边的
        }
    }

    std::cout<<"Redirect logname:"<<strLogName.toStdString()<<std::endl;
    file.setFileName(strLogName);
}

QFileInfoList CFileAppender::entryInfoListFotCreate(QString strPath)
{
    QDir dir(strPath);
    QFileInfoList strListLogs = dir.entryInfoList(QStringList()<<"*.txt", QDir::Files | QDir::Readable | QDir::NoDotAndDotDot, QDir::Name);
    //把所有的日志按照创建时间排序
    QMultiMap<qint64,QFileInfo> mapAll; //<1970以来秒数,文件信息>
    foreach(QFileInfo fileInfo,strListLogs){
        qint64 nTimeTSec = fileInfo.fileTime(QFile::FileBirthTime).toSecsSinceEpoch();
        mapAll.insert(nTimeTSec,fileInfo);  //考虑同一秒创建的文件
    }

    return mapAll.values();
}
